package com.muy.redis.view.window.redis.view.table;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intellij.openapi.project.Project;
import com.intellij.ui.table.TableView;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.ListTableModel;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.tuple.Pair;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

/**
 * @Author jiyanghuang
 * @Date 2022/8/1 22:39
 * 用一个对象包装每一行
 */
@Data
public class TreeLineWrap<T extends TreeLineObject> {

    private T t;

    private Map<String, ColumnDesc> cm;

    public Map<String, ColumnDesc> genColumnDesc(Class clazz) {
        if (null == cm) {
            cm = Maps.newHashMap();

            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                AColumnDesc aColumnDesc = field.getAnnotation(AColumnDesc.class);
                if (null == aColumnDesc) {
                    continue;
                }
                ColumnDesc columnDesc = new ColumnDesc();
                fillValue(field.getName(), aColumnDesc.nameShow(), columnDesc::setNameShow);
                fillValue(field.getName(), aColumnDesc.columnUk(), columnDesc::setColumnUk);
                columnDesc.setOrder(aColumnDesc.order());
                columnDesc.setCellEditable(aColumnDesc.cellEditable());
                columnDesc.setColumnDescEnum(ColumnDescEnum.FIELD_ANN);
                columnDesc.setColumnValueEnum(aColumnDesc.valueType());
                columnDesc.setColumnShowAttr(aColumnDesc.columnShowAttr());
                ColumnValueEnum columnValueEnum = ColumnValueEnum.fetchByColumnType(field.getType().getName());
                if (null != columnValueEnum) {
                    columnDesc.setColumnValueEnum(columnValueEnum);
                }
                columnDesc.setFetchName(field.getName());
                columnDesc.setValueChangeInvokeMethod(aColumnDesc.vcFun());
                try {
                    if (null != t) {
                        Object fieldValue = FieldUtils.readField(t, field.getName(), true);
                        columnDesc.setValueShow(columnDesc.getColumnValueEnum().transferValue(fieldValue));
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                    columnDesc.setValueShow("ex");
                }
                cm.put(columnDesc.getColumnUk(), columnDesc);
            }
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                AColumnDesc aColumnDesc = method.getAnnotation(AColumnDesc.class);
                if (null == aColumnDesc) {
                    continue;
                }
                ColumnDesc columnDesc = new ColumnDesc();
                fillValue(method.getName(), aColumnDesc.nameShow(), columnDesc::setNameShow);
                fillValue(method.getName(), aColumnDesc.columnUk(), columnDesc::setColumnUk);
                columnDesc.setOrder(aColumnDesc.order());
                columnDesc.setColumnDescEnum(aColumnDesc.type());
                columnDesc.setColumnValueEnum(aColumnDesc.valueType());
                columnDesc.setColumnShowAttr(aColumnDesc.columnShowAttr());
                try {
                    ColumnValueEnum columnValueEnum = ColumnValueEnum.fetchByColumnType(method.getReturnType().getName());
                    if (null != columnValueEnum) {
                        columnDesc.setColumnValueEnum(columnValueEnum);
                    }
                    columnDesc.setValueChangeInvokeMethod(aColumnDesc.vcFun());
                    if (null != t) {
                        Object result = method.invoke(t);
                        columnDesc.setValueShow(columnDesc.getColumnValueEnum().transferValue(result));
                    }

                } catch (Exception ex) {
                    ex.printStackTrace();
                    columnDesc.setValueShow("ex");
                }
                cm.put(columnDesc.getColumnUk(), columnDesc);
            }
        }
        return cm;
    }

    /**
     * 如果没有指定名称则按优先级取值
     *
     * @param methodName
     * @param annValue
     * @param consumer
     */
    private void fillValue(String methodName, String annValue, Consumer<String> consumer) {
        if (StringUtils.isNotBlank(annValue)) {
            consumer.accept(annValue);
            return;
        }
        if (methodName.startsWith("get")) {
            consumer.accept(methodName.substring("get".length()));
            return;
        }
        consumer.accept(methodName);
    }

    /**
     * 找出头部分信息
     * 1.把每一行数据进行包装，转换成Map
     * 2.合并所有的Map,收集的所有的列，理论上需要排序的？
     * 3.返回头部信息，及包装的对象
     *
     * @param jsonPersonList
     * @param project
     * @return
     */
    public static <T extends TreeLineObject> Pair<ColumnInfo[], List<TreeLineWrap>> header(Class clazz, List<T> jsonPersonList, Project project) {
        Map<String, ColumnDesc> allColumnMap = Maps.newTreeMap();
        List<TreeLineWrap> result = Lists.newArrayList();
        if (CollectionUtils.isEmpty(jsonPersonList)) {
            for (T person : jsonPersonList) {
                TreeLineWrap treeLineWrap = new TreeLineWrap();
                treeLineWrap.genColumnDesc(clazz);
                allColumnMap.putAll(treeLineWrap.getCm());
                result.add(treeLineWrap);
            }
        } else {
            for (T person : jsonPersonList) {
                TreeLineWrap treeLineWrap = new TreeLineWrap();
                treeLineWrap.setT(person);
                treeLineWrap.genColumnDesc(clazz);
                allColumnMap.putAll(treeLineWrap.getCm());
                result.add(treeLineWrap);
            }
        }
        List<Map.Entry<String, ColumnDesc>> sortList = Lists.newArrayList(allColumnMap.entrySet());
        Collections.sort(sortList, (a, b) -> {
            return a.getValue().getOrder() - b.getValue().getOrder();
        });
        Set<String> keys = Sets.newLinkedHashSet();
        for (Map.Entry<String, ColumnDesc> entry : sortList) {
            keys.add(entry.getKey());
        }
        ColumnInfo[] columnInfos = new ColumnInfo[keys.size()];
        int index = 0;
        for (final String key : keys) {
            columnInfos[index++] = new TableColumnInfo(key, project, allColumnMap.get(key).isCellEditable());
        }
        return Pair.of(columnInfos, result);
    }

    public static <T extends TreeLineObject> ListTableModel buildJsonTable(Class clazz, List<T> jsonPersonList, Project project) {
        if (CollectionUtils.isEmpty(jsonPersonList)) {
            return null;
        }
        Pair<ColumnInfo[], List<TreeLineWrap>> pair = header(clazz, jsonPersonList, project);
        return new ListTableModel<>(pair.getLeft(), pair.getRight());
    }

    public static <T extends TreeLineObject> TableView tableView(Class clazz, List<T> jsonPersonList, Project project) {
        ListTableModel listTableModel = buildJsonTable(clazz, jsonPersonList, project);
        if (null == listTableModel) {
            return null;
        }
        return new TableView(listTableModel);
    }

    public static <T extends TreeLineObject> TableView tableView(ListTableModel listTableModel) {
        return new TableView(listTableModel);
    }

    public void invokeVcFun(TreeLineObject old, TreeLineObject upate, ColumnDesc columnDesc) {
        if (StringUtils.isBlank(columnDesc.getValueChangeInvokeMethod())) {
            return;
        }
        for (Method method : t.getClass().getDeclaredMethods()) {
            if (columnDesc.getValueChangeInvokeMethod().equals(method.getName())) {
                try {
                    method.invoke(t, old, upate);
                    break;
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }

    public void updateValueToObj(ColumnDesc columnDesc, String value, Object obj) {
        if (ColumnDescEnum.FIELD_ANN.equals(columnDesc.getColumnDescEnum())) {
            columnDesc.getColumnValueEnum().writeFieldValue(obj, columnDesc.getFetchName(), value);
        } else if (ColumnDescEnum.METHOD_ANN.equals(columnDesc.getColumnDescEnum())) {

        }
    }

    public static <T extends TreeLineObject> TreeLineWrap createRowValue(Class clazz, T t, Project project) {
        if (null == t) {
            return null;
        }

        TreeLineWrap treeLineWrap = new TreeLineWrap();
        treeLineWrap.setT(t);
        treeLineWrap.genColumnDesc(clazz);
        return treeLineWrap;
    }

}
